/*
 * @Author: ljk
 * @Date: 2023-05-10 20:29:43
 * @LastEditors: ljk
 * @LastEditTime: 2023-05-11 19:20:25
 * @Description: 进程池
 */
#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <assert.h>
#include <sys/wait.h>
#include "Task.hpp"
#include <stdlib.h>
using namespace std;

const int gnum = 3;
Task t;

class EndPoint
{
private:
    static int number;

public:
    pid_t _child_id;
    int _write_fd;
    std::string processname;

public:
    EndPoint(int id, int fd) : _child_id(id), _write_fd(fd)
    {
        // porcess-0[pid:fd]
        char namebuffer[64];
        snprintf(namebuffer, sizeof(namebuffer), "process-%d[%d:%d]", number++, _child_id, _write_fd);
        processname = namebuffer;
    }
    const std::string &name() const
    {
        return processname;
    }
    ~EndPoint()
    {
    }
};
int EndPoint::number = 0;

// 子进程被迫执行的方法
void waitCommand()
{
    while (true)
    {
        int command = 0;
        int n = read(0, &command, sizeof(int));
        if (n == sizeof(int))
        {
            t.Execute(command);
        }
        else if (n == 0)
        {
            std::cout << "进程: " << getpid() << ", 退出" << std::endl;
            break;
        }
        else
        {
            //...
            break;
        }
    }
}

void createProcess(vector<EndPoint> &end_points)
{
    vector<int> fds;
    // 1.1 创建管道
    for (int i = 0; i < gnum; i++)
    {
        int pipefd[2] = {0};
        int n = pipe(pipefd);
        assert(n == 0);
        (void)n;

        // 1.2 创建进程
        pid_t id = fork();
        assert(id != -1);

        // 子进程
        if (id == 0)
        {
            for (auto& fd : fds)
            {
                close(fd);
            }
            // 1.3 关闭不需要的fd
            close(pipefd[1]);
            // 期望所有的子进程读取指令的时候，都从标准输入读取
            // 1.3.1 输入重定向
            dup2(pipefd[0], 0);
            // 1.3.2 子进程开始等待获取命令
            waitCommand();
            close(pipefd[0]);
            exit(0);
        }

        // 父进程
        // 1.3 关闭不需要的fd
        close(pipefd[0]);

        // 1.4 将新的子进程和它的管道写端，构建对象
        end_points.push_back(EndPoint(id, pipefd[1]));
        fds.push_back(pipefd[1]);
    }
}

int showBoard()
{
    std::cout << "######################" << std::endl;
    std::cout << "# 1.执行日志任务     #" << std::endl;
    std::cout << "# 2.执行数据库任务   #" << std::endl;
    std::cout << "# 3.执行网络请求任务 #" << std::endl;
    std::cout << "# 0.退出             #" << std::endl;
    std::cout << "######################" << std::endl;
    std::cout << "请选择# ";
    int command = 0;
    std::cin >> command;
    return command;
}

void ctrlProcess(const vector<EndPoint> &end_points)
{
    int num = 0;
    int cnt = 0;
    while (true)
    {
        size_t seed = time(0);
        srand(seed);
        // 2.1 选择任务 -- 随机/交互
        // 2.1.1 随机
        // int command = rand() % end_points.size();

        // 2.2.2 交互
        int command = showBoard();
        if (command == 0)
        {
            break;
        }
        // 2.2 选择进程
        int index = cnt++;
        cnt %= end_points.size();
        std::string name = end_points[index].name();
        std::cout << "选择了进程：" << name << "| 处理任务：" << command << std::endl;

        // 2.3 下发任务
        write(end_points[index]._write_fd, &command, sizeof(command));
        sleep(1);
    }
}
void waitProcess(const vector<EndPoint> &end_points)
{
    // 3.1 让子进程全部退出,只需要让父进程关闭所有的写端
    // for (const auto &ep : end_points)

    //正向等待会使wait进程阻塞，因为继承的管道写端未关闭完

    //第一种解决方式:不太实用，真正构建出一对一模型才是正确解决方式
    //for (int end = end_points.size() - 1; end >= 0; end--)
    for (int end = 0; end < end_points.size(); end++)
    {
        close(end_points[end]._write_fd);

        waitpid(end_points[end]._child_id, nullptr, 0);
        std::cout << "进程: " << end_points[end]._child_id << ", 资源被回收" << std::endl;
    }
    std::cout << "资源回收完毕..." << endl;
    sleep(10);

    // 3.2 父进程回收子进程的僵尸状态
    // for (const auto& ep : end_points)
    // {
    //     waitpid(ep._child_id, nullptr, 0);
    // }
    // std::cout << "所有的子进程资源已被回收" << std::endl;
}
int main()
{
    // 1. 进行构建控制结构, 父进程写入，子进程读取 -- 现在是有bug的
    vector<EndPoint> end_points;
    createProcess(end_points);
    // 2. 给子进程指令让其执行
    ctrlProcess(end_points);
    // 3. 处理所有的退出进程
    waitProcess(end_points);

    return 0;
}
